package contower

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/nats-io/nats.go"
	"github.com/shopspring/decimal"
	clientv3 "go.etcd.io/etcd/client/v3"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
	"math/rand"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
	"wzgames/internal/constant"
	"wzgames/internal/gdao/aviatordao"
	"wzgames/internal/gdao/userdao"
	"wzgames/internal/gloader"
	"wzgames/internal/model/aviatorp"
	"wzgames/internal/model/gamep"
	"wzgames/internal/model/mcomm"
	"wzgames/internal/model/user"
	"wzgames/internal/response"
	"wzgames/internal/secure"
	"wzgames/internal/services/algo"
	"wzgames/pkg/actor/actor"
	"wzgames/pkg/actor/actorservice"
	"wzgames/pkg/components/mysql"
	"wzgames/pkg/components/redis"
	"wzgames/pkg/logger"
	"wzgames/pkg/utils/byteutil"
	"wzgames/pkg/utils/calcutil"
	"wzgames/pkg/utils/dateutil"
	"wzgames/pkg/utils/stringutil"
)

// 小飞机播报实例

type AirManager struct {
	mu   sync.RWMutex
	pid  *actor.PID
	inst *gamep.GameInst         // 小飞机游戏实例
	cfg  *aviatorp.AviatorConfig // 小飞机游戏实例配置
	ab   *AirBetMgr              // 投注 CashOut 取消投注汇总

	onlineSub *nats.Subscription // 监听在线人数变化
	betSub    *nats.Subscription // 监听投注 CashOut等情况

	instIdStr string
	dao       *aviatordao.AviatorDao
	userDao   *userdao.AccountDao
	ds        *mysql.DS
	userDs    *mysql.DS
	redisC    *redis.RedisClient

	robotList map[uint64]*user.UserEntity
	flews     *aviatorp.AviatorFlews       // 历史轮次信息
	runEntity *aviatorp.AviatorRoundEntity // 当前轮次
	runRound  *aviatorp.AviatorRound       // 当前轮次
	algoConf  *aviatorp.AviatorAlgoConfig

	isCtl      int
	multiplier *AMultiplier

	randAbsorbTime time.Time

	keyLogin  string // nats key，玩家login
	keyLogout string
	keyMulti  string // nats key，赔率播报
	keyAct    string // nats key，玩家动作

	roundDir string
	stateDir string

	onlines int64

	master bool
	status mcomm.RunState
}

func NewAirManager(inst *gamep.GameInst) *AirManager {
	s := strconv.FormatInt(inst.InstID, 10)
	roundDir := fmt.Sprintf("%s/round-%v", constant.CcAirCraft, inst.InstID)
	am := &AirManager{
		inst:       inst,
		instIdStr:  s,
		flews:      &aviatorp.AviatorFlews{},
		keyLogin:   constant.AvtLoginNKey + "." + s,
		keyLogout:  constant.AvtLogoutNKey + "." + s,
		keyMulti:   constant.AvtMultiplierNKey + "." + s,
		keyAct:     constant.AvtActNKey + "." + s,
		roundDir:   roundDir,
		stateDir:   fmt.Sprintf("%s/state", roundDir),
		multiplier: &AMultiplier{},
		status:     mcomm.RunState_RsOnIdle,
	}
	e := am.updateConf()
	if e != nil {
		return nil
	}

	name := fmt.Sprintf("aircraft-%s", strconv.FormatInt(inst.InstID, 10))
	props := actor.PropsFromProducer(func() actor.Actor { return am })
	pid, err := actorservice.ActorSystem().Root.SpawnNamed(props, name)
	if err != nil {
		logger.Error("[ConTower] spawn air manager actor error", zap.Int64("InstId", inst.InstID), zap.Error(err))
		return nil
	}
	am.pid = pid
	am.ab = newAirBetMgr(inst.InstID)
	am.multiplier.Init(am)                       // 初始化播报计算
	am.multiplier.SetHouseEdge(am.cfg.HouseEdge) // HouseEdge
	am.robotList = make(map[uint64]*user.UserEntity)
	return am
}

func (am *AirManager) Pid() *actor.PID {
	return am.pid
}

// updateConf 动态更新配置
func (am *AirManager) updateConf() error {
	am.mu.Lock()
	defer am.mu.Unlock()
	if am.cfg == nil {
		am.cfg = &aviatorp.AviatorConfig{}
	}
	err := proto.Unmarshal(am.inst.Conf, am.cfg)
	if err != nil {
		logger.Error("[ConTower] update Conf on proto.Unmarshal", zap.Int64("InstId", am.inst.InstID), zap.Error(err))
		return err
	}
	return nil
}

func (am *AirManager) Receive(ctx actor.Context) {
	switch m := ctx.Message().(type) {
	case StartAirCraft: // 启动轮次播报
		am.master = true
		am.startAirCraft()
	case NewRound: // 下一轮
		err := am.NewRound(m.RoundId)
		if err == nil {
			am.algoConf = am.GetAlgoConfig()
			am.ab.Clean()
			am.changeToState(constant.BetState)
		} else {
			TimerAfter(time.Second*3, am.NextRound, nil)
		}
	case *aviatorp.AviatorRound:
		am.updateFlews(m) // 轮次结束后更新历史轮次列表
	default:
	}
}

// 更新配置
func (am *AirManager) GetAlgoConfig() *aviatorp.AviatorAlgoConfig {
	algoConf, err := am.dao.GetAviatorAlgoConfig()
	if err != nil {
		logger.Error("fail to get aviator algorithm config ! err: ", zap.Error(err))
		return &aviatorp.AviatorAlgoConfig{}
	}
	// 校验参数
	if algoConf.TotalPoolDefaultVal <= 0 || algoConf.TotalPoolAbsorbVal <= 0 || algoConf.RewardPoolGiftVal <= 0 {
		logger.Error("config parameters value can not equal or less than zero! ")
		return &aviatorp.AviatorAlgoConfig{}
	}
	if (algoConf.AbsorbPeriodProp < 1 && algoConf.AbsorbPeriodProp > 50) ||
		(algoConf.GiftPeriodProp < 1 && algoConf.GiftPeriodProp > 50) ||
		(algoConf.RewardPoolTaxProp < 1 && algoConf.RewardPoolTaxProp > 10) {
		logger.Error("config parameters prop over range! ")
		return &aviatorp.AviatorAlgoConfig{}
	}
	return algoConf
}

func (am *AirManager) updateAlgoPoolFromRedis() {
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	pVal, err := am.redisC.Get().Get(ctx, "aviator_pools_value").Result()
	cancel()
	if err != nil || len(pVal) <= 0 {
		logger.Error("[ConTower] get pool fail ", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
		return
	}
	b := []byte(pVal)
	p := &aviatorp.AviatorPoolsValue{}
	err = json.Unmarshal(b, p)
	if err != nil {
		logger.Error("[ConTower] unmarshal pool data failed ", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
		return
	}
	algo.GetPoolMgr().UpdatePools(p.TotalPoolValue, p.RewardPoolValue)
}

// Update 小飞机实例动态更新
func (am *AirManager) Update(inst *gamep.GameInst) {
	am.mu.Lock()
	oldStat := am.inst.Status
	am.inst = inst
	am.mu.Unlock()
	_ = am.updateConf()
	am.multiplier.SetHouseEdge(am.cfg.HouseEdge)
	if !am.master {
		return
	}
	// 状态变更可以启动或停止播报
	if oldStat != mcomm.Status_Up && inst.Status == mcomm.Status_Up {
		am.startAirCraft()
	} else if oldStat == mcomm.Status_Up && inst.Status != mcomm.Status_Up {
		am.stop()
	}
}

func (am *AirManager) Config() *aviatorp.AviatorConfig {
	return am.cfg
}

func (am *AirManager) startAirCraft() {
	defer logger.Recover()
	if am.inst.Status != mcomm.Status_Up || am.status == mcomm.RunState_RsOnRun {
		return
	}
	if am.status == mcomm.RunState_RsOnStop { // 停止后重新启动
		am.status = mcomm.RunState_RsOnRun
		TimerAfter(time.Second*5, am.SettleNext, nil)
		logger.Info("[ConTower] airCraft restart", zap.String("InstId", am.instIdStr))
		return
	}
	am.status = mcomm.RunState_RsOnRun
	am.redisC = gloader.CommMgr().RedisPool()
	am.ds = gloader.CommMgr().GetDBProxy(constant.MysqlOfAviator)
	am.userDs = gloader.CommMgr().GetDBProxy(constant.MysqlOfUser)
	am.dao = &aviatordao.AviatorDao{Driver: am.ds.Ds()}
	am.userDao = &userdao.AccountDao{Driver: am.userDs.Ds()}
	// 监听在线人数变动
	sub1, err := gloader.CommMgr().NatsC().Subscribe(constant.AvtOnlineNKeySub+"."+am.instIdStr, am.handleOnlineInfo)
	if err != nil {
		logger.Error("[ConTower] start airCraft fail on subscribe", zap.Int64("InstID", am.inst.InstID), zap.String("SubKey", constant.AvtOnlineNKeySub), zap.Error(err))
		return
	}
	am.onlineSub = sub1
	// 监听投注 CashOut等
	sub2, err1 := gloader.CommMgr().NatsC().Subscribe(constant.AvtBetNKeySub+"."+am.instIdStr, am.handleBetInfo)
	if err1 != nil {
		logger.Error("[ConTower] start airCraft fail on subscribe", zap.Int64("InstID", am.inst.InstID), zap.String("SubKey", constant.AvtBetNKeySub), zap.Error(err1))
		return
	}
	am.betSub = sub2
	err = am.initRounds() // 初始化轮次，并开启新一轮
	if err != nil {
		logger.Error("fail to init !")
		return
	}

	am.updateOnlineToCache(nil)
	logger.Info("[ConTower] airCraft start", zap.String("InstId", am.instIdStr))

	// 加载DB数据并初始化机器人
	for _, u := range am.getRobotlist() {
		am.robotList[u.Uid] = u
	}

	// 随机吸水时间
	am.randAbsorbTime = time.Now()

	// 获取db的算法配置
	am.algoConf = am.GetAlgoConfig()

	// 初始化算法水池值
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	pVal, err := am.redisC.Get().Exists(ctx, "aviator_pools_value").Result()
	cancel()
	if pVal <= 0 {
		// 更新算法初始值
		algo.GetPoolMgr().UpdatePools(am.algoConf.TotalPoolDefaultVal, 0)

		// 更新到redis
		am.ab.UpdatePoolsValue(am.algoConf.TotalPoolDefaultVal, 0)
	} else {

		// 从redis获取已有的水池值
		am.updateAlgoPoolFromRedis()
	}
}

// 获取机器人
func (am *AirManager) getRobotlist() []*user.UserEntity {
	robotList, err := am.userDao.GetAccountByFlag(1)
	if err != nil {
		logger.Errorf("get robot failed ! err == %s", err.Error())
		return nil
	}
	if len(robotList) <= 0 {
		logger.Warn("no robot available, please check config!")
	}
	return robotList
}

func (am *AirManager) handleOnlineInfo(msg *nats.Msg) {
	switch msg.Subject {
	case am.keyLogin:
		atomic.AddInt64(&am.onlines, 1)
	case am.keyLogout:
		atomic.AddInt64(&am.onlines, -1)
		if atomic.LoadInt64(&am.onlines) < 0 {
			atomic.StoreInt64(&am.onlines, 0)
		}
	}
}

func (am *AirManager) handleBetInfo(msg *nats.Msg) {
	if am.runRound == nil {
		return
	}
	switch msg.Subject {
	case am.keyAct:
		am.handlePlayRoundAct(msg.Data)
		return
	default:

	}
	betNode := response.AcquireAvtBet()
	err := proto.Unmarshal(msg.Data, betNode)
	if err != nil {
		logger.Error("[ConTower] process bet fail on proto.Unmarshal", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
		response.ReleaseAvtBet(betNode)
		return
	}
	actorservice.Send(am.ab.Pid(), betNode)
}

func (am *AirManager) handlePlayRoundAct(data []byte) {
	d := &aviatorp.AvtRoundAct{}
	err := proto.Unmarshal(data, d)
	if err != nil {
		logger.Error("[ConTower] process bet play round act fail on proto.Unmarshal", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
		return
	}
	actorservice.Send(am.ab.Pid(), d)
}

func (am *AirManager) stop() {
	am.mu.Lock()
	am.status = mcomm.RunState_RsOnStop
	am.mu.Unlock()
	logger.Info("[ConTower] airCraft stop", zap.String("InstId", am.instIdStr))
}

func (am *AirManager) genServerSeed() string {
	return stringutil.RandomString(40)
}

func (am *AirManager) initRounds() error {
	// 取最近25个轮次
	topRounds, err := am.dao.GetTopRounds(am.inst.InstID)
	if err != nil {
		logger.Error("[ConTower] init rounds fail", zap.Int64("InstID", am.inst.InstID), zap.Error(err))
		return err
	}
	var roundId int64
	if len(topRounds) == 0 {
		roundId = 0
	} else {
		roundId = topRounds[0].Rid
	}

	var flews []*aviatorp.AviatorRound
	for _, rd := range topRounds {
		if rd.Status != constant.CrashState {
			continue
		}
		first3 := &aviatorp.First3Bets{}
		if rd.Clientseeds != nil {
			err = json.Unmarshal(rd.Clientseeds, first3)
			if err != nil {
				logger.Error("[ConTower] init flews fail on Unmarshal", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", rd.Rid), zap.Error(err))
				continue
			}
		}
		flews = append(flews, &aviatorp.AviatorRound{
			RoundId:  rd.Rid,
			Start:    rd.Start,
			End:      rd.End,
			Seed:     rd.Sseed,
			First3:   first3,
			H512:     rd.H512,
			Result:   rd.Result,
			Hhex:     rd.Hhex,
			Hdecimal: rd.Hdecimal,
			Seed256:  rd.Sseed256,
		})
	}
	am.initFlews(flews) // 初始化历史轮次到Cache
	// 开启下一轮次
	actorservice.Send(am.pid, NewRound{RoundId: roundId})
	return nil
}

func (am *AirManager) NewRound(lastRoundId int64) error {
	defer logger.Recover()
	newRoundId := lastRoundId + 1 // 轮次ID递增
	seed := am.genServerSeed()    // 新轮次的服务端种子
	if seed == "" {
		logger.Error("[ConTower] gen seed fail", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", newRoundId))
		return fmt.Errorf("gen server seed fail")
	}
	t := dateutil.UnixSecs()
	am.runEntity = &aviatorp.AviatorRoundEntity{
		Rid:         newRoundId,
		InstId:      am.inst.InstID,
		Start:       t,
		End:         0,
		Sseed:       seed,
		Sseed256:    secure.Sha256Hex(byteutil.StringToSliceByte(seed)),
		Clientseeds: []byte{},
		H512:        "",
		Hhex:        "",
		Hdecimal:    0,
		Result:      0,
		Status:      constant.UnActive,
		Count:       0,
		CashOuts:    0,
		Runt:        0,
		CrashTime:   0,
		Created:     t,
		Updated:     t,
	}
	err := am.dao.CreateRound(am.runEntity)
	if err != nil {
		logger.Error("[ConTower] create round fail", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", newRoundId), zap.Error(err))
		return err
	}

	am.runRound = &aviatorp.AviatorRound{
		RoundId: newRoundId,
		Start:   t,
		State:   constant.UnActive,
		Seed:    seed,
		Seed256: am.runEntity.Sseed256,
	}
	// 创建新轮次后 计算下一轮次种子
	return am.nextRoundInfo(newRoundId)
}

func (am *AirManager) nextRoundInfo(runRoundId int64) error {
	nextRoundId := runRoundId + 1
	seed := am.genServerSeed()
	if seed == "" {
		logger.Error("[ConTower] gen next round seed fail", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", nextRoundId))
		return fmt.Errorf("gen next server seed fail")
	}
	am.runRound.Next = &aviatorp.AviatorNextRound{
		RoundId: nextRoundId,
		Seed:    seed,
		H256:    secure.Sha256Hex(byteutil.StringToSliceByte(seed)),
	}
	return nil
}

func (am *AirManager) Destroy() {
	if am.betSub != nil {
		_ = am.betSub.Unsubscribe()
		am.betSub.Drain()
	}
	if am.onlineSub != nil {
		_ = am.onlineSub.Unsubscribe()
		am.onlineSub.Drain()
	}
	actorservice.Stop(am.ab.Pid())
	actorservice.Stop(am.pid)
}

// changeToState 小飞机状态切换
func (am *AirManager) changeToState(state int32) {
	defer logger.Recover()
	switch state {
	case constant.BetState: // 运行投注阶段
		am.runEntity.Start = dateutil.UnixSecs()
		am.runEntity.Updated = am.runEntity.Start
		am.runEntity.Status = constant.BetState
		_, err := am.dao.ChangeToBetState(am.runEntity)
		if err != nil {
			logger.Error("[ConTower] change round to bet state fail", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid), zap.Error(err))
			return
		}
		am.runRound.State = constant.BetState
		am.runRound.Start = am.runEntity.Start
		st := &aviatorp.AirCraftState{
			RoundId:    am.runRound.RoundId,
			NewStateId: constant.BetState,
			StateStart: dateutil.UnixMilli(),
			TimeLeft:   5000,
		}
		// 更新到ETCD，通知Aviator状态变更
		if am.updateEtcdState(st) {
			// 状态切换
			TimerAfter(time.Second*5, am.RunState, nil)
			am.ab.StopBetsInfoTimer()
			TimerAfter(time.Second, am.ab.BroadcastBetsInfo, nil)
		}
	case constant.FlyState: // 飞行阶段
		am.multiplier.Reset()
		am.ab.StopBetsInfoTimer()
		st := &aviatorp.AirCraftState{
			RoundId:       am.runRound.RoundId,
			NewStateId:    constant.FlyState,
			StateStart:    dateutil.UnixMilli(),
			ExpectTime:    am.runRound.ExpectTime,
			MaxMultiplier: am.runEntity.Result,
		}
		// 更新到ETCD，通知Aviator状态变更
		if am.updateEtcdState(st) {
			am.ab.StopCashoutsTimer()
			TimerAfter(time.Second, am.ab.BroadcastCashoutsInfo, nil)
			am.multiplier.Run(am.runRound)
		}
	case constant.CrashState: // Crash
		am.updatePools()
		am.Flush()
		am.runRound.State = constant.CrashState
		am.runEntity.Status = constant.CrashState
		am.runEntity.End = dateutil.UnixSecs()
		am.runRound.End = am.runEntity.End
		st := &aviatorp.AirCraftState{
			RoundId:       am.runRound.RoundId,
			NewStateId:    constant.CrashState,
			RoundEnd:      am.runEntity.End,
			MaxMultiplier: am.runEntity.Result,
		}
		// 更新到ETCD，通知Aviator状态变更
		if !am.updateEtcdState(st) {
			return
		}
		am.runEntity.Updated = am.runEntity.End
		am.runEntity.CrashTime = am.runEntity.End
		am.runEntity.CashOuts = am.ab.RoundCashCount()
		_, err := am.dao.ChangeToEndState(am.runEntity)
		if err != nil {
			logger.Error("[ConTower] change round to crash state fail", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid), zap.Error(err))
		}
		am.updateFlew()
		// 准备下一轮
		TimerAfter(time.Second*5, am.SettleNext, nil)
	}
}

func (am *AirManager) updatePools() {
	// 该局总利润统计
	allProfitVal := am.ab.GetAllProfitValue()
	allProfitVal = allProfitVal.Mul(decimal.NewFromFloat(100.0))

	//pV, _ := allProfitVal.Float64()
	//logger.Info("all profit", zap.Float64("profit", pV), zap.Int("isCtl", am.isCtl))

	if !allProfitVal.Equal(decimal.NewFromInt(0)) {
		// 放水由奖池提供, 亏钱时扣总水池, 其余按抽成比分到总水池和奖池
		if am.isCtl == constant.GiftStatus && allProfitVal.LessThan(decimal.NewFromInt(0)) {

			algo.GetPoolMgr().AddToRewardPool(allProfitVal.IntPart())
			//logger.Info("GiftStatus", zap.Int64("profit", allProfitVal.IntPart()))
		} else if allProfitVal.LessThan(decimal.NewFromInt(0)) {

			// 亏钱时不抽成到奖池
			algo.GetPoolMgr().AddToTotalPool(allProfitVal.IntPart())
			//logger.Info("LossMoney", zap.Int64("profit", allProfitVal.IntPart()))
		} else {
			// 根据奖池利润占比汇入奖池
			percent := decimal.NewFromInt32(int32(am.algoConf.RewardPoolTaxProp)).Div(decimal.NewFromInt32(100))
			r := allProfitVal.Mul(percent).IntPart()
			algo.GetPoolMgr().AddToRewardPool(r)

			// 根据总水池利润占比汇入总水池
			t := allProfitVal.IntPart() - r
			algo.GetPoolMgr().AddToTotalPool(t)
			//logger.Info("Normal", zap.Int64("total pool change", t), zap.Int64("reward pool change", r))
		}

		// 累加总水池奖池值存入redis
		am.ab.UpdatePoolsValue(algo.GetPoolMgr().GetTotalPool(), algo.GetPoolMgr().GetRewardPool())
	}
}

// updateEtcdState 更新ETCD存储状态，通知前置的Aviator
func (am *AirManager) updateEtcdState(state *aviatorp.AirCraftState) bool {
	b, err := proto.Marshal(am.runRound)
	if err != nil {
		logger.Error("[ConTower] update round info to etcd fail on marshal run round", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid), zap.Error(err))
		return false
	}
	b1, er := proto.Marshal(state)
	if er != nil {
		logger.Error("[ConTower] update round info to etcd fail on marshal round state", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid), zap.Error(er))
		return false
	}
	// 同步更新轮次和状态
	ctx, cancel := context.WithTimeout(gloader.CClient().Context(), defaultTimeout)
	tresp, e1 := gloader.CClient().RawClient().Txn(ctx).
		Then(clientv3.OpPut(am.roundDir, byteutil.SliceByteToString(b)),
			clientv3.OpPut(am.stateDir, byteutil.SliceByteToString(b1))).
		Commit()
	cancel()

	if e1 != nil {
		logger.Error("[ConTower] update round info to etcd fail", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid), zap.Error(e1))
		return false
	}

	if !tresp.Succeeded {
		logger.Error("[ConTower] update round info to etcd fail in txn", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid))
		return false
	}

	return true
}

// 根据倍率从随机ClientSeed中计算得出倍率相等或相近的组合
func (am *AirManager) getProperClientSeedByMultipler(isCtl int, multi decimal.Decimal) {

	// 获取随机机器人补充Client
	selectRobotId, calTime := am.getRandRobotAsClient(isCtl)

	// 通过比对计算得出的倍数相近或相等的，作为ClientSeed组合数据
	resp, first3 := am.getClientSeedByCmpMulti(isCtl, calTime, selectRobotId, multi)

	// 赋值
	am.transferResult(resp, first3)
}

// 获取随机机器人作为Client,选中第3个机器人，准备生成clientseed随机参与计算
func (am *AirManager) getRandRobotAsClient(isCtl int) (uint64, int) {
	if len(am.robotList) <= 0 {
		return uint64(0), 0
	}

	// 如果下注人数少于3人,将当前端下注人数补齐为3人
	calTime := 1
	selectedRobotId := uint64(0)
	pushNum := 3 - am.ab.getFarrLen()
	randList := make(map[uint64]bool)
	for i := 0; i < pushNum; i++ {
		randRobotId := am.getNORepeatRandValue(randList)
		if _, ok := am.robotList[randRobotId]; ok {
			if isCtl != constant.NormalStatus && i == pushNum-1 { //第3个机器人先不随机种子
				selectedRobotId = randRobotId
				calTime = 1000
				break
			}
			am.ab.fillFarr(am.robotList[randRobotId], stringutil.RandomString(20))
		}
	}
	return selectedRobotId, calTime
}

// 获取不重复的随机值
func (am *AirManager) getNORepeatRandValue(randList map[uint64]bool) uint64 {
	if len(am.robotList) <= 0 {
		return uint64(0)
	}
	for {
		randIndex := rand.Intn(len(am.robotList))
		cnt := int(0)
		for k, _ := range am.robotList {
			if cnt == randIndex {
				if _, ok := randList[k]; !ok {
					randList[k] = true
					return k
				}
			}
			cnt++
			if cnt > randIndex {
				break
			}
		}
	}
}

// 通过比对计算得出的倍数相近或相等的，作为ClientSeed组合数据
func (am *AirManager) getClientSeedByCmpMulti(isCtl int, calTime int, selectRobotId uint64, multi decimal.Decimal) (*aviatorp.AviatorRoundEntity, *aviatorp.First3Bets) {
	if multi.LessThan(decimal.NewFromFloat(1.0)) {
		multi = decimal.NewFromFloat(1.0)
	}

	resp := &aviatorp.AviatorRoundEntity{}
	first3 := &aviatorp.First3Bets{}
	nearestRes := decimal.NewFromFloat(0.0)
	for i := 0; i < calTime; i++ {

		// 随机得到第3个clientSeed并赋值
		if _, ok := am.robotList[selectRobotId]; (isCtl != constant.NormalStatus) && ok {
			am.ab.fillFarr(am.robotList[selectRobotId], stringutil.RandomString(20))
		}

		// 根据serverSeed+ClientSeed拼接后加密取13个十六进制字母转为16位的十进制数字，将数字参与运算得到小飞机的倍率
		first3Tmp, combineStr := am.ab.First3Bets()
		s := am.runEntity.Sseed + combineStr
		h512 := secure.Sha512Hex(byteutil.StringToSliceByte(s))
		hHex := h512[0:13]
		hDecimal, err := calcutil.Hex2Dec("0" + hHex)
		if err != nil {
			logger.Error("[ConTower] calc hex decimal fail", zap.Int64("InstID", am.inst.InstID),
				zap.Int64("RoundId", am.runEntity.Rid), zap.String("Hex", hHex), zap.Error(err))
			continue
		}
		_, result := am.multiplier.CalcResult(hDecimal, am.runEntity.Count, isCtl)

		// 判断根据随机得到的倍数结果是否与期望的结果相等或相近
		if (isCtl == constant.NormalStatus) || result.Equal(multi) { // 不杀不放的情况下, 或者受控时找到相同的倍数时返回结果
			resp, first3 = am.transferData(result, h512, hHex, hDecimal, first3Tmp)
			break
		} else if result.GreaterThanOrEqual(multi.Sub(decimal.NewFromFloat(0.01))) && // 误差小于0.01的作为结果
			result.LessThanOrEqual(multi.Add(decimal.NewFromFloat(0.01))) {
			resp, first3 = am.transferData(result, h512, hHex, hDecimal, first3Tmp)
			break
		} else if result.LessThan(multi) { // 如果一直没有相等或相近的值，就最接近真实倍数的值
			if result.GreaterThan(nearestRes) {
				nearestRes = result
			}
			resp, first3 = am.transferData(nearestRes, h512, hHex, hDecimal, first3Tmp)
		}
	}
	if resp == nil || len(resp.H512) <= 0 {
		fT, _ := json.Marshal(first3)
		logger.Error("finally aviator not get H512 ", zap.String("ServerSeed", am.runEntity.Sseed), zap.String("ClientSeed", string(fT)))
		return &aviatorp.AviatorRoundEntity{}, &aviatorp.First3Bets{}
	}

	if resp.Result > 0 && resp.Result < 1.0 {
		resp.Result = 1.0
	}

	// 限制赠送最大值边界
	//if isCtl != constant.GiftStatus {
	//	return resp, first3
	//}
	//confGiftMax := decimal.NewFromInt32(int32(am.algoConf.GiftMaxVal))
	//if decimal.NewFromFloat(resp.Result).GreaterThan(confGiftMax) {
	//	resp.Result, _ = confGiftMax.Float64()
	//}
	return resp, first3
}

// 赋值
func (am *AirManager) transferData(result decimal.Decimal, h512, hHex string, hDecimal int64, first3Tmp *aviatorp.First3Bets) (
	*aviatorp.AviatorRoundEntity, *aviatorp.First3Bets) {
	res, _ := result.Float64()
	resp := &aviatorp.AviatorRoundEntity{
		Result:   res,
		H512:     h512,
		Hhex:     hHex,
		Hdecimal: hDecimal,
	}
	return resp, first3Tmp
}

// 赋值结果
func (am *AirManager) transferResult(resp *aviatorp.AviatorRoundEntity, first3 *aviatorp.First3Bets) {
	am.runEntity.H512 = resp.H512
	am.runEntity.Hhex = resp.Hhex
	am.runEntity.Hdecimal = resp.Hdecimal
	b, err := json.Marshal(first3)
	if err != nil {
		logger.Error("[ConTower] marshal first3 bets fail", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid), zap.Error(err))
		b = []byte{}
	}
	am.runEntity.Clientseeds = b
	am.runEntity.Result = calcutil.RoundFloor(resp.Result)
	am.runEntity.Runt = am.runEntity.Updated
	_, err = am.dao.ChangeToRunState(am.runEntity)
	if err != nil {
		logger.Error("[ConTower] change round state to run fail", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid), zap.Error(err))
		return
	}
	am.runRound.ExpectTime = am.multiplier.CalcTotalTime(am.runEntity.Result)
	am.runRound.First3 = first3
	am.runRound.H512 = resp.H512
	am.runRound.Hhex = resp.Hhex
	am.runRound.Hdecimal = resp.Hdecimal
	am.runRound.Result = am.runEntity.Result

	//logger.Info("aviator result", zap.Float64("actual multiple", am.runEntity.Result))
}

func (am *AirManager) RunState(args interface{}) {
	defer logger.Recover()
	am.runRound.State = constant.FlyState
	am.runEntity.Updated = dateutil.UnixSecs()
	am.runEntity.Status = constant.FlyState
	am.runEntity.Count = am.ab.RoundBetCount()

	// 判定是否需要杀或放水
	isStage := constant.NormalStage
	isCtl := constant.NormalStatus
	if am.runRound.RoundId == int64(am.algoConf.AbsorbRoundId) {
		isStage = constant.KillStage
		isCtl = constant.KillStatus
	} else {
		isStage, isCtl = algo.GetPoolMgr().IsNeedCtl(am.algoConf)
	}

	//beginTime := time.Now()
	// 杀分的小飞机倍数等于1.0加上风险系数(为小于30的随机数除以100得到的值)
	multi := decimal.NewFromFloat(0.0)
	if isCtl == constant.KillStatus {
		riskPara := decimal.NewFromInt(algo.GetPoolMgr().GetTotalPool()).Div(decimal.NewFromInt(am.algoConf.TotalPoolAbsorbVal))
		rD := riskPara.Mul(decimal.NewFromInt(100)).IntPart()
		rS := rand.NewSource(time.Now().UnixNano())
		r := rS.Int63() % rD
		rProp := decimal.NewFromInt(r).Div(decimal.NewFromInt(100))
		multi = decimal.NewFromFloat(1.0).Add(rProp)
		//rPV, _ := rProp.Float64()
		//logger.Info("kill status", zap.Int64("rD", rD), zap.Int64("r", r), zap.Float64("rPV", rPV))
	} else if isCtl == constant.GiftStatus {
		riskPara := decimal.NewFromInt(algo.GetPoolMgr().GetRewardPool()).Div(decimal.NewFromInt(am.algoConf.RewardPoolGiftVal))
		totalM := decimal.NewFromInt(1000).Div(decimal.NewFromFloat(1.0).Add(riskPara))
		rS := rand.NewSource(time.Now().UnixNano())
		rV := rS.Int63() % totalM.IntPart()
		if rV > 0 {
			multi = decimal.NewFromFloat(1.0).Add(riskPara).Add(decimal.NewFromInt(rV))
		} else {
			multi = decimal.NewFromFloat(1.0).Add(riskPara)
		}
		//logger.Info("gift status", zap.Int64("totalM", totalM.IntPart()), zap.Int64("rV", rV))
	}

	// 判断是否随机吸水, 吸水得出的倍数=总水池吸水值/总水池值
	if isStage == constant.NormalStage {
		costTime := time.Since(am.randAbsorbTime).Milliseconds()
		if am.algoConf.RandAbsorbInterTime > 0 &&
			decimal.NewFromInt(costTime).GreaterThan(decimal.NewFromInt32(int32(am.algoConf.RandAbsorbInterTime*1000))) {
			isCtl = algo.GetPoolMgr().IsCanAbsorb(am.algoConf)
			if isCtl == constant.KillStatus {
				riskPara := decimal.NewFromInt(am.algoConf.TotalPoolAbsorbVal).Div(decimal.NewFromInt(algo.GetPoolMgr().GetTotalPool()))
				multi = decimal.NewFromFloat(1.0).Add(riskPara)
				//r, _ := riskPara.Float64()
				//m, _ := multi.Float64()
				//logger.Info("rand select absorb para ", zap.Float64("risk parameter", r), zap.Float64("multiple", m))
			}
		}
	}

	// 如果没有机器人,就不控水了,提示报错
	if len(am.robotList) <= 0 {
		isStage = constant.NormalStage
		isCtl = constant.NormalStatus
		am.randAbsorbTime = time.Now()
		logger.Error("no robot can be instead for real client seed!")
	} else if isStage != constant.NormalStage {
		am.randAbsorbTime = time.Now()
	}

	//expM, _ := multi.Float64()
	//logger.Info("algorithm control", zap.Int("stage", isStage), zap.Int("status", isCtl), zap.Float64("multiple", expM),
	//	zap.Int64("rand absorb time", am.randAbsorbTime.Unix()))

	// 根据倍率从随机ClientSeed中计算得出倍率相等或相近的组合
	am.getProperClientSeedByMultipler(isCtl, multi)

	am.isCtl = isCtl
	//logger.Info("algo cost time", zap.Int64("time", time.Since(beginTime).Milliseconds()))

	// 开始起飞
	am.changeToState(constant.FlyState)
}

func (am *AirManager) updateFlew() {
	flew := &aviatorp.AviatorRound{
		RoundId:  am.runRound.RoundId,
		Start:    am.runRound.Start,
		End:      am.runRound.End,
		Seed:     am.runRound.Seed,
		First3:   am.runRound.First3,
		H512:     am.runRound.H512,
		Result:   am.runRound.Result,
		Hhex:     am.runRound.Hhex,
		Hdecimal: am.runRound.Hdecimal,
		Seed256:  am.runRound.Seed256,
	}
	actorservice.Send(am.pid, flew)
}

func (am *AirManager) SettleNext(data interface{}) {
	if am.status == mcomm.RunState_RsOnStop {
		return
	}
	//am.runEntity.Updated = dateutil.UnixSecs()
	//am.runEntity.Status = constant.SettleNext
	//am.runRound.State = constant.SettleNext
	//st := &aviatorp.AirCraftState{
	//	RoundId:    am.runRound.RoundId,
	//	NewStateId: constant.SettleNext,
	//}
	//if am.updateEtcdState(st) {
	am.ab.PrepareForNextRound()
	actorservice.Send(am.pid, NewRound{RoundId: am.runRound.RoundId})
	//}
}

func (am *AirManager) NextRound(data interface{}) {
	if am.status == mcomm.RunState_RsOnStop {
		return
	}
	actorservice.Send(am.pid, NewRound{RoundId: am.runRound.RoundId})
}

// PublishCurrentMultiplier 播报数据发往Nats，前置Aviator进行接收
func (am *AirManager) PublishCurrentMultiplier(roundId int64, multiplier, maxMulti float64, t int64, crash bool) {
	st := response.AcquireAvtMulti()
	defer response.ReleaseAvtMulti(st)
	st.RoundId = roundId
	st.CurrentGameTime = t
	st.CurrentPayout = multiplier
	st.Crash = crash
	st.MaxMultiplier = maxMulti
	b, err := proto.Marshal(st)
	if err != nil {
		logger.Error("[ConTower] publish round CurrentMultiplier fail on marshal state", zap.Int64("InstID", am.inst.InstID), zap.Int64("RoundId", am.runEntity.Rid), zap.Error(err))
		return
	}
	err = gloader.CommMgr().NatsC().Publish(am.keyMulti, b)
	if err != nil {
	}
}

func (am *AirManager) Flush() {
	_ = gloader.CommMgr().NatsC().Flush()
}
